﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using DCS.CalculationClient.DCS_Service;
using DCS.CalculationClient.View;

namespace DCS.CalculationClient
{
    public class TaskProcessor :IDisposable
    {
        public string TaskDirectory { get; set; }
        private readonly string processId = Process.GetCurrentProcess().Id.ToString();

        // GUID текущего задания
        Guid currentTaskGuid;

        public void ProcessTask(TaskPack newTask)
        {
            var computationModule = newTask.ComputationModule.ModuleName;

            // Директория для расчета задания
            TaskDirectory = Path.Combine(processId, newTask.TaskName);

            // создаем временную директорию для хранения задания
            Directory.CreateDirectory(TaskDirectory);

            // номер порции
            var numberPortion = newTask.Portion;

            LogAdded(new LogRecord(newTask.TaskName, numberPortion, TaskPortionState.Downloading,
                                   "Получение задания", newTask.IsRetransferred));

            // сохраняем вычислительный модуль на диск,
            // если задание пришло в первый раз на клиент
            if (newTask.Guid != currentTaskGuid)
            {
                // удаляем с диска старый вычислительный модуль
                if (Directory.Exists(currentTaskGuid.ToString()))
                {
                    Directory.Delete(currentTaskGuid.ToString(), true);
                }

                // записываем новый вычислительный модуль
                currentTaskGuid = newTask.Guid;
                File.WriteAllBytes(Path.Combine(TaskDirectory, computationModule), newTask.ComputationModule.ComputationModuleData);
            }

            if (newTask.Dependencies != null)
            {
                //Сохраняем на диск зависимости
                foreach (var dependency in newTask.Dependencies)
                {
                    File.WriteAllBytes(Path.Combine(TaskDirectory, dependency.DependencyName),
                                       dependency.DependencyData);
                }
            }


            //cчитываем входные данные для запуска исполняемого файла из
            //текстового файла dcs_input.txt
            string arguments = null;

            if (File.Exists(Path.Combine(TaskDirectory, "dcs_input.txt")))
            {
                using (var sr = new StreamReader(Path.Combine(TaskDirectory,"dcs_input.txt")))
                {
                    arguments = sr.ReadLine();
                }
            }
            else if (File.Exists(Path.Combine(TaskDirectory ,"Generator.exe")))
            {
                PortionStateChanged(newTask.TaskName, numberPortion, TaskPortionState.Processing, "Подготовка вычислений");


                arguments = Generator.GetCommandLineArguments(TaskDirectory ,
                                                    numberPortion, newTask.PortionsNumber,
                                                    newTask.AdditionalArguments);
            }

            //Создаем список файлов, которые находятся в рабочей папке, до начала расчетов
            var filesBeforeCalculating = new List<string>(Directory.GetFiles(TaskDirectory));
            filesBeforeCalculating.Sort();

            PortionStateChanged(newTask.TaskName, numberPortion, TaskPortionState.Processing, "Расчёт");

            try
            {
                Calculate(TaskDirectory, newTask.ComputationModule.ModuleName, arguments);

                PortionStateChanged(newTask.TaskName, numberPortion, TaskPortionState.Uploading, "Запись результатов на сервер");

                var files = Directory.GetFiles(TaskDirectory);

                //Запись результатов на сервер
                var result = new TaskPortionResult
                                 {
                                     TaskGuid = currentTaskGuid,
                                     PortionNumber = numberPortion,
                                     IsRetransferred = newTask.IsRetransferred,
                                     ResultFiles = (from taskFiles in files
                                                    where !filesBeforeCalculating.Contains(taskFiles)
                                                    select new ResultFile
                                                               {
                                                                   FileName = Path.GetFileName(taskFiles),
                                                                   FileData = File.ReadAllBytes(taskFiles)
                                                               }).ToArray()
                                 };

                PortionResultUpdated(result);
                
                PortionStateChanged(newTask.TaskName, numberPortion, TaskPortionState.Completed, "Завершено");

                var temp = Path.Combine(TaskDirectory, computationModule);

                foreach (var path in files.Where(t => temp != t))
                {
                    File.Delete(path);
                }
            }
            catch(Exception)
            {
                PortionStateChanged(newTask.TaskName, numberPortion, TaskPortionState.Error, "Завершено");
            }
        }

        private static void Calculate(string directory, string calculationModule, string arguments)
        {
            // Создаем новый объект процесса                
            // Зададем приложение для запуска            
            var process = new Process { StartInfo = { FileName = calculationModule } };

            // Задаем аргументы приложения            
            process.StartInfo.Arguments += arguments;

            // Скрываем окно приложения
            process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

            //устанавливаем рабочую директорию
            process.StartInfo.WorkingDirectory = directory;

            // Запускаем приложение
            process.Start();

            // Ожидаем окончания работы приложения
            process.WaitForExit();
        }

        public void Dispose()
        {
            if (Directory.Exists(processId))
                Directory.Delete(processId, true);
        }

        #region Events

        public event Action<LogRecord> LogAdded;
        public event Action<string, uint, TaskPortionState, string> PortionStateChanged;
        public event Action<TaskPortionResult> PortionResultUpdated;

        #endregion
    }
}
